#include <stdio.h>
#include "updTcpService.h"

#include "security.c"

void updSrvHeart(aeEventLoop *loop, int fd, void *data, int mask);

int updTcpBufInitialize(updDispatcher *server, int fd)
{
    server->tcpclients[fd].tcpbuf.status = 0;
    server->tcpclients[fd].tcpbuf.dolen  = 0;
    return 0;
}

int updTcpSetOnline(updDispatcher *server, int fd)
{
    server->tcpclients[fd].online = 1;
    server->tcpclients[fd].lasttime = time(NULL);
    updTcpBufInitialize(server, fd);
    return 0;
}

int updTcpUpdateTime(updDispatcher *server, int fd)
{
    server->tcpclients[fd].lasttime = time(NULL);
    return 0;
}

int updTcpSetNoClose(updDispatcher *server, int fd)
{
    server->tcpclients[fd].online = 2;
    return 0;
}

int updTcpSetOutline(updDispatcher *server, int fd)
{
    server->tcpclients[fd].online = 0;
    return 0;
}

int updTcpBeforeSend(updDispatcher *server, int fd)
{
    updTcpClient *client  = &server->tcpclients[fd];
    tcpHead *head = (tcpHead *)client->tcpbuf.buf;

    updTcpBufInitialize(server, fd);

    xorencrypt(client->tcpbuf.buf, sizeof(tcpHead) + head->bodylen);

    return 0;
}


int updTcpSendData(int fd, void *data)
{
    int cnt = 0;
    updDispatcher *server = data;
    updTcpClient *client  = &server->tcpclients[fd];
    tcpHead *head = (tcpHead *)client->tcpbuf.buf;
    uint32_t bodylen = head->bodylen;

    xordecrypt((char *)&bodylen, sizeof(uint32_t));

    cnt = send(fd, client->tcpbuf.buf + client->tcpbuf.dolen, 
               sizeof(tcpHead) + bodylen - client->tcpbuf.dolen, MSG_DONTWAIT|MSG_NOSIGNAL);
    if(cnt < 0){
        if(errno == EAGAIN || errno == EINTR){
            return 1;
        }
        return -1;
    }

    client->tcpbuf.dolen += cnt;

    if(client->tcpbuf.dolen != sizeof(tcpHead) + bodylen){
        return 1;
    }

    return 0;
}


void updTcpSend(aeEventLoop *loop, int fd, void *data, int mask)
{
    int status = 0;
    updDispatcher *server = data;

    updTcpUpdateTime(server, fd);
    status = updTcpSendData(fd, data);
    switch(status)
    {
        case -1:
        case  0:
            aeDeleteFileEvent(loop, fd, mask);
            updTcpSetOutline(server, fd);
            close(fd);
            break;
        case  1:
            break;
        default:
            updError("illegal status\n");
            abort();
    }

    return;
}


void updTcpNoCloseSend(aeEventLoop *loop, int fd, void *data, int mask)
{
    int status = 0;
    list *l = data;
    updDispatcher *server = l->comm;

    status = updTcpSendData(fd, server);
    switch(status)
    {
        case -1:
            aeDeleteFileEvent(loop, fd, mask);
            updTcpSetOutline(server, fd);
            break;
        case  0:
            aeDeleteFileEvent(loop, fd, mask);
            updTcpSetOutline(server, fd);
            if(-1 == aeCreateFileEvent(server->loop, fd, AE_READABLE, updSrvHeart, l)){
                abort();
            }
            break;
        case  1:
            break;
        default:
            updError("illegal status\n");
            abort();
    }

    return;
}

int updTcpRecvData(int fd, void *data)
{
    int cnt;
    updDispatcher *server = data;
    updTcpClient  *client = &server->tcpclients[fd];
    tcpHead *head;

    GOTOBEGIN:
    switch(client->tcpbuf.status)
    {
        case 0:
            cnt = recv(fd, client->tcpbuf.buf + client->tcpbuf.dolen, 
                       sizeof(tcpHead) - client->tcpbuf.dolen, MSG_DONTWAIT);
            if(cnt > 0){
                client->tcpbuf.dolen += cnt;
                if(client->tcpbuf.dolen == sizeof(tcpHead)){
                    client->tcpbuf.status = 1;
                    xordecrypt(client->tcpbuf.buf, client->tcpbuf.dolen);
                    goto GOTOBEGIN;
                }
                return 1;
            }
            else if(cnt < 0 && (errno == EAGAIN || errno == EINTR)){
                return 1;
            }
            return -1;
            break;
        case 1:
            head = (tcpHead *)client->tcpbuf.buf;
            if(0 == head->bodylen){
                client->tcpbuf.status = 2;
                goto GOTOBEGIN;
            }
            if(MAX_BUF < head->bodylen + sizeof(tcpHead)){
                updError("接收数据长度超过限制");
                return -1;
            }

            cnt = recv(fd, client->tcpbuf.buf + client->tcpbuf.dolen, 
                        head->bodylen + sizeof(tcpHead) - client->tcpbuf.dolen, MSG_DONTWAIT);
            if(cnt > 0){
                client->tcpbuf.dolen += cnt;
                if(client->tcpbuf.dolen == (int)sizeof(tcpHead) + head->bodylen){
                    client->tcpbuf.status = 2;
                    goto GOTOBEGIN;
                }
                return 1;
            }
            else if(cnt < 0 && (errno == EAGAIN || errno == EINTR)){
                return 1;
            }
            return -1;
            break;
        case 2:
            head = (tcpHead *)client->tcpbuf.buf;
            xordecrypt(client->tcpbuf.buf + sizeof(tcpHead), head->bodylen);
            break;
        default:
            updError("undefined status\n");
            abort();
    }

    return 0;
}


int updSrvHeartTimeout(struct aeEventLoop *loop, long long id, void *data)
{
    ralIterator *it;
    time_t tm = time(NULL);
    updDispatcher *server = data;
    
    it = ralCreateIterator(server->srvList);
    if(it){
        list *l;
        while((l = ralIteratorNext(it))){
            if(tm - l->last >= server->srvHeartTimeout){
                aeDeleteFileEvent(server->loop, l->fd, AE_READABLE|AE_WRITABLE);
                ralDelete(server->srvList, l);
                ralRewindIterator(server->srvList, server->loopIterator);
            }
        }
    }    
    ralIteratorRelease(it);

    return server->srvHeartTimeout * 300;
}


void updSrvHeart(aeEventLoop *loop, int fd, void *data, int mask)
{
    int ret;
    char heartBuf[1024];
    list *l = data;
    updDispatcher *server = l->comm;

    ret = recv(fd, heartBuf, 1024, MSG_DONTWAIT);
    if(ret > 0){
        l->last = time(NULL);
        return;
    }
    else if(-1 == ret && (errno == EINTR || errno == EAGAIN)){
        return;
    }
    aeDeleteFileEvent(server->loop, l->fd, AE_READABLE|AE_WRITABLE);
    ralDelete(server->srvList, l);
    ralRewindIterator(server->srvList, server->loopIterator);

    return;
}


int updTcpProtocol(aeEventLoop *loop, int fd, void *data)
{
    updDispatcher *server = data;
    updTcpClient *client = &server->tcpclients[fd];
    tcpHead *head = (tcpHead *)client->tcpbuf.buf;

    if(0 != strcmp(head->service, server->service)){
        updError("服务[%s]非法", head->service);
        updTcpSetOutline(server, fd);
        close(fd);
        return -1;
    }

    switch(head->cmd)
    {
        case SRV_REGISTER:
        {
            list* l;
            l = ralFind(server->srvList, (srvAddress *)(client->tcpbuf.buf + sizeof(tcpHead)));
            if(l){
                ralDelete(server->srvList, l);
                ralRewindIterator(server->srvList, server->loopIterator);
            }
            head->ecode = 1;
            l = ralAddHead(server->srvList, fd, server, 
                            (srvAddress *)(client->tcpbuf.buf + sizeof(tcpHead)));
            if(l){
                updTcpSetNoClose(server, fd);
                ralRewindIterator(server->srvList, server->loopIterator);
                head->ecode = 0;
            }
            head->bodylen = 0;
            updTcpBeforeSend(server, fd);
            if(-1 == aeCreateFileEvent(loop, fd, AE_WRITABLE, updTcpNoCloseSend, l)){
                updError("aeCreateFileEvent error\n");
                abort();
            }
        }
        break;
        case TCP_GET_ADDR:
        {
            list* l;
            l = ralIteratorLoopNext(server->loopIterator);
            if(l){
                head->ecode = 0;
                head->bodylen = sizeof(srvAddress);
                memcpy(client->tcpbuf.buf + sizeof(tcpHead), &l->addr, head->bodylen);
            }
            else{
                head->ecode = 2;
                head->bodylen = 0;
            }

            updTcpBeforeSend(server, fd);
            if(-1 == aeCreateFileEvent(loop, fd, AE_WRITABLE, updTcpSend, data)){
                updError("aeCreateFileEvent error\n");
                abort();
            }
        }
        break;
        case TCP_GET_ALL_ADDR:
        {
            char *buf;
            buf = ralAcquireAllAddress(server->srvList);
            if(buf){
                head->bodylen = strlen(buf);
                if(head->bodylen > MAX_BUF - sizeof(tcpHead))
                    head->bodylen = MAX_BUF - sizeof(tcpHead);
                memcpy(client->tcpbuf.buf + sizeof(tcpHead), buf, head->bodylen);
                ralReleaseAcquireAddress(buf);
            }
            else{
                head->bodylen = 0;
            }
            head->ecode = 0;

            updTcpBeforeSend(server, fd);
            if(-1 == aeCreateFileEvent(loop, fd, AE_WRITABLE, updTcpSend, data)){
                updError("aeCreateFileEvent error\n");
                abort();
            }
        }
        break;
        default:
            updError("undefined tcp cmd[%d]", head->cmd);
            updTcpSetOutline(server, fd);
            close(fd);
    }

    return 0;
}


void updTcpService(aeEventLoop *loop, int fd, void *data, int mask)
{
    int status = 0;
    updDispatcher *server = data;

    updTcpUpdateTime(server, fd);
    status = updTcpRecvData(fd, data);
    switch(status)
    {
        case -1:
            aeDeleteFileEvent(loop, fd, mask);
            updTcpSetOutline(server, fd);
            close(fd);
            break;
        case  0:
            aeDeleteFileEvent(loop, fd, mask);
            if(-1 == updTcpProtocol(loop, fd, data)){
                updError("updTcpProtocol Error");
            }
            break;
        case  1:
            break;
        default:
            updError("undefined status\n");
            abort();
    }

    return;
}


int updTcpTimeout(struct aeEventLoop *loop, long long id, void *data)
{
    int i;
    time_t tm = time(NULL);
    updDispatcher *server = data;

    for(i = server->tlfd; i < server->maxfd; i++){
        if(server->tcpclients[i].online == 1 
           && (tm - server->tcpclients[i].lasttime >= server->tcpConnTimeout)){
            aeDeleteFileEvent(loop, i, AE_READABLE|AE_WRITABLE);
            server->tcpclients[i].online = 0;
            close(i);
        }
    }

    return server->tcpConnTimeout * 200;
}


int updTcpCreateListenFd(short port)
{
    int opt = 1;
    int tlfd = -1;
    struct sockaddr_in srvaddr = {};

    if(-1 == (tlfd = socket(AF_INET, SOCK_STREAM, 0))){
        updError("socket Error[%s]", strerror(errno));
        return -1;
    }

    fcntl(tlfd, F_SETFL, fcntl(tlfd, F_GETFL)|O_NONBLOCK);
    setsockopt(tlfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(&opt));

    int keepalive = 1;
    int keepidle  = 30;
    int keepintvl = 10;
    int keepcount = 3;
    setsockopt(tlfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
    setsockopt(tlfd, SOL_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
    setsockopt(tlfd, SOL_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
    setsockopt(tlfd, SOL_TCP, TCP_KEEPCNT, &keepcount, sizeof(keepcount));

    unsigned char tos = 0x10;
    setsockopt(tlfd, SOL_IP, IP_TOS, &tos, sizeof(tos));

    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port   = htons(port);
    srvaddr.sin_addr.s_addr = INADDR_ANY;
    if(-1 == bind(tlfd, (struct sockaddr *)&srvaddr, sizeof(struct sockaddr))){
        updError("bind Error[%s]", strerror(errno));
        close(tlfd);
        return -1;
    }

    if(-1 == listen(tlfd, SOMAXCONN)){
        updError("listen Error[%s]", strerror(errno));
        close(tlfd);
        return -1;
    }

    return tlfd;
}


void updTcpAccept(aeEventLoop *loop, int fd, void *data, int mask)
{
    int cfd;
    updDispatcher *server = data;
    struct sockaddr_in cliaddr;
    socklen_t clilen;

    cfd = accept(fd, (struct sockaddr *)&cliaddr, &clilen);
    if(-1 == cfd){
        updError("accept error:%s", strerror(errno));
        return;
    }

    if(cfd >= server->maxfd){
        updError("fd >= maxfd\n");
        close(cfd);
        return;
    }

    updTcpSetOnline(server, cfd);

    if(-1 == aeCreateFileEvent(loop, cfd, AE_READABLE, updTcpService, data)){
        updError("aeCreateFileEvent error:%s\n", strerror(errno));
        updTcpSetOutline(server, cfd);
        close(cfd);
        return;
    }

    return;
}

